﻿using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Collections;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Reflection;
using System.Text.RegularExpressions;
using XYS.Core.Models;

/// <summary>
/// API接口枚举描述过滤器
/// </summary>
public class APIResultEnumDescFilter : ActionFilterAttribute
{
    /// <summary>
    /// 
    /// </summary>
    /// <param name="context"></param>
    public override void OnActionExecuted(ActionExecutedContext context)
    {
        if (GlobalCoreSetting.FormatRequestsIgnore.Any(x => Regex.IsMatch(context.HttpContext.Request.Path.Value, "^" + Regex.Escape(x).Replace("\\*", ".*") + "$"))) return;

        if (context.Result is ObjectResult objectResult)
        {
            objectResult.Value = ProcessObject(objectResult.Value);
        }
        base.OnActionExecuted(context);
    }

    private object ProcessObject(object obj)
    {
        if (obj == null) return null;
        var type = obj.GetType();
        if (type == typeof(ResultModel<object>)) return obj;

        // 处理集合类型（List/Array等）
        if (obj is IEnumerable enumerable && obj is not string)
        {
            var list = new ArrayList();
            foreach (var item in enumerable)
            {
                list.Add(ProcessObject(item));
            }
            return list;
        }

        // 如果是简单类型直接返回
        if (type.IsPrimitive || type == typeof(string) || type == typeof(decimal) || type == typeof(DateTime) || type == typeof(TimeSpan) || type.IsEnum) return obj;

        // 使用 ExpandoObject 动态添加属性
        dynamic expando = new System.Dynamic.ExpandoObject();
        var expandoDict = (IDictionary<string, object>)expando;

        var properties = type.GetProperties();
        foreach (var property in properties)
        {
            var value = property.GetValue(obj);
            if (value == null)
            {
                if (GlobalCoreSetting.ResultIgnoreNullValue)
                    expandoDict.Remove(property.Name);
                else
                    expandoDict[property.Name] = value;
                continue;
            }

            if (property.PropertyType == typeof(ResultModel) && property.Name == "Success") continue;

            // 添加原始值
            expandoDict[property.Name] = value;
            // 处理枚举
            if (IsEnumOrNullableEnum(property.PropertyType, out var enumType))
            {
                var enumValue = value?.ToString();
                var description = GetEnumDescription(enumType, enumValue);

                expandoDict[$"{property.Name}Label"] = description;
                expandoDict[$"{property.Name}Value"] = enumValue;
                continue;
            }
            // 处理嵌套对象
            if (value.GetType() != obj.GetType() &&
                !property.PropertyType.IsPrimitive &&
                property.PropertyType != typeof(string) &&
                !property.PropertyType.IsEnum)
            {
                //是否为数组
                if (value is IEnumerable enumerableArray && value is not string)
                {
                    //是否为枚举数组
                    bool isEnumArray = false;
                    List<string> enumLabelArray = new();
                    List<string> enumValueArray = new();
                    foreach (var item in enumerableArray)
                    {
                        if (item == null)
                        {
                            enumLabelArray.Add("");
                            enumValueArray.Add("");
                            continue;
                        }
                        var enumerableItemType = item.GetType();
                        if (enumerableItemType.IsEnum)
                        {
                            isEnumArray = true;
                            var description = GetEnumDescription(enumerableItemType, item.ToString());
                            enumLabelArray.Add(description);
                            enumValueArray.Add(item.ToString());
                        }
                    }

                    //如果为枚举数组，则继续递归了
                    if (isEnumArray)
                    {
                        expandoDict[$"{property.Name}Label"] = enumLabelArray;
                        expandoDict[$"{property.Name}Value"] = enumValueArray;
                        continue;
                    }
                }
                expandoDict[property.Name] = ProcessObject(value);
            }
        }
        return expando;
    }

    private bool IsEnumOrNullableEnum(Type type, out Type enumType)
    {
        if (type.IsEnum)
        {
            enumType = type;
            return true;
        }

        var underlyingType = Nullable.GetUnderlyingType(type);
        if (underlyingType != null && underlyingType.IsEnum)
        {
            enumType = underlyingType;
            return true;
        }

        enumType = null;
        return false;
    }

    private static ConcurrentDictionary<string, string> EnumDescMaps = new();

    private string GetEnumDescription(Type enumType, string enumValue)
    {
        if (string.IsNullOrEmpty(enumValue)) return null;
        string cacheKey = $"{enumType.FullName}.{enumValue}";
        if (!EnumDescMaps.TryGetValue(cacheKey, out var desc))
        {
            var field = enumType.GetField(enumValue);
            desc = field?.GetCustomAttribute<DescriptionAttribute>()?.Description
                   ?? enumValue;
            EnumDescMaps[cacheKey] = desc;
        }
        return desc;
    }
}